{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Making a Flower Classifier with VGG16\n",
    "\n",
    "### Loading the VGG16 Model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Using TensorFlow backend.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Downloading data from https://github.com/fchollet/deep-learning-models/releases/download/v0.1/vgg16_weights_tf_dim_ordering_tf_kernels_notop.h5\n",
      "58892288/58889256 [==============================] - 37s 1us/step\n"
     ]
    }
   ],
   "source": [
    "from keras.applications import VGG16\n",
    "\n",
    "# VGG16 was designed to work on 224 x 224 pixel input images sizes\n",
    "img_rows = 224\n",
    "img_cols = 224 \n",
    "\n",
    "#Loads the VGG16 model \n",
    "vgg16 = VGG16(weights = 'imagenet', \n",
    "                 include_top = False, \n",
    "                 input_shape = (img_rows, img_cols, 3))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Inpsecting each layer"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0 InputLayer False\n",
      "1 Conv2D True\n",
      "2 Conv2D True\n",
      "3 MaxPooling2D True\n",
      "4 Conv2D True\n",
      "5 Conv2D True\n",
      "6 MaxPooling2D True\n",
      "7 Conv2D True\n",
      "8 Conv2D True\n",
      "9 Conv2D True\n",
      "10 MaxPooling2D True\n",
      "11 Conv2D True\n",
      "12 Conv2D True\n",
      "13 Conv2D True\n",
      "14 MaxPooling2D True\n",
      "15 Conv2D True\n",
      "16 Conv2D True\n",
      "17 Conv2D True\n",
      "18 MaxPooling2D True\n"
     ]
    }
   ],
   "source": [
    "# Let's print our layers \n",
    "for (i,layer) in enumerate(vgg16.layers):\n",
    "    print(str(i) + \" \"+ layer.__class__.__name__, layer.trainable)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Let's freeze all layers except the top 4 "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0 InputLayer False\n",
      "1 Conv2D False\n",
      "2 Conv2D False\n",
      "3 MaxPooling2D False\n",
      "4 Conv2D False\n",
      "5 Conv2D False\n",
      "6 MaxPooling2D False\n",
      "7 Conv2D False\n",
      "8 Conv2D False\n",
      "9 Conv2D False\n",
      "10 MaxPooling2D False\n",
      "11 Conv2D False\n",
      "12 Conv2D False\n",
      "13 Conv2D False\n",
      "14 MaxPooling2D False\n",
      "15 Conv2D False\n",
      "16 Conv2D False\n",
      "17 Conv2D False\n",
      "18 MaxPooling2D False\n"
     ]
    }
   ],
   "source": [
    "from keras.applications import VGG16\n",
    "\n",
    "# VGG16 was designed to work on 224 x 224 pixel input images sizes\n",
    "img_rows = 224\n",
    "img_cols = 224 \n",
    "\n",
    "# Re-loads the VGG16 model without the top or FC layers\n",
    "vgg16 = VGG16(weights = 'imagenet', \n",
    "                 include_top = False, \n",
    "                 input_shape = (img_rows, img_cols, 3))\n",
    "\n",
    "# Here we freeze the last 4 layers \n",
    "# Layers are set to trainable as True by default\n",
    "for layer in vgg16.layers:\n",
    "    layer.trainable = False\n",
    "    \n",
    "# Let's print our layers \n",
    "for (i,layer) in enumerate(vgg16.layers):\n",
    "    print(str(i) + \" \"+ layer.__class__.__name__, layer.trainable)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Let's make a function that returns our FC Head"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "def addTopModel(bottom_model, num_classes, D=256):\n",
    "    \"\"\"creates the top or head of the model that will be \n",
    "    placed ontop of the bottom layers\"\"\"\n",
    "    top_model = bottom_model.output\n",
    "    top_model = Flatten(name = \"flatten\")(top_model)\n",
    "    top_model = Dense(D, activation = \"relu\")(top_model)\n",
    "    top_model = Dropout(0.3)(top_model)\n",
    "    top_model = Dense(num_classes, activation = \"softmax\")(top_model)\n",
    "    return top_model"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Let's add our FC Head back onto VGG"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "_________________________________________________________________\n",
      "Layer (type)                 Output Shape              Param #   \n",
      "=================================================================\n",
      "input_2 (InputLayer)         (None, 224, 224, 3)       0         \n",
      "_________________________________________________________________\n",
      "block1_conv1 (Conv2D)        (None, 224, 224, 64)      1792      \n",
      "_________________________________________________________________\n",
      "block1_conv2 (Conv2D)        (None, 224, 224, 64)      36928     \n",
      "_________________________________________________________________\n",
      "block1_pool (MaxPooling2D)   (None, 112, 112, 64)      0         \n",
      "_________________________________________________________________\n",
      "block2_conv1 (Conv2D)        (None, 112, 112, 128)     73856     \n",
      "_________________________________________________________________\n",
      "block2_conv2 (Conv2D)        (None, 112, 112, 128)     147584    \n",
      "_________________________________________________________________\n",
      "block2_pool (MaxPooling2D)   (None, 56, 56, 128)       0         \n",
      "_________________________________________________________________\n",
      "block3_conv1 (Conv2D)        (None, 56, 56, 256)       295168    \n",
      "_________________________________________________________________\n",
      "block3_conv2 (Conv2D)        (None, 56, 56, 256)       590080    \n",
      "_________________________________________________________________\n",
      "block3_conv3 (Conv2D)        (None, 56, 56, 256)       590080    \n",
      "_________________________________________________________________\n",
      "block3_pool (MaxPooling2D)   (None, 28, 28, 256)       0         \n",
      "_________________________________________________________________\n",
      "block4_conv1 (Conv2D)        (None, 28, 28, 512)       1180160   \n",
      "_________________________________________________________________\n",
      "block4_conv2 (Conv2D)        (None, 28, 28, 512)       2359808   \n",
      "_________________________________________________________________\n",
      "block4_conv3 (Conv2D)        (None, 28, 28, 512)       2359808   \n",
      "_________________________________________________________________\n",
      "block4_pool (MaxPooling2D)   (None, 14, 14, 512)       0         \n",
      "_________________________________________________________________\n",
      "block5_conv1 (Conv2D)        (None, 14, 14, 512)       2359808   \n",
      "_________________________________________________________________\n",
      "block5_conv2 (Conv2D)        (None, 14, 14, 512)       2359808   \n",
      "_________________________________________________________________\n",
      "block5_conv3 (Conv2D)        (None, 14, 14, 512)       2359808   \n",
      "_________________________________________________________________\n",
      "block5_pool (MaxPooling2D)   (None, 7, 7, 512)         0         \n",
      "_________________________________________________________________\n",
      "flatten (Flatten)            (None, 25088)             0         \n",
      "_________________________________________________________________\n",
      "dense_3 (Dense)              (None, 256)               6422784   \n",
      "_________________________________________________________________\n",
      "dropout_2 (Dropout)          (None, 256)               0         \n",
      "_________________________________________________________________\n",
      "dense_4 (Dense)              (None, 17)                4369      \n",
      "=================================================================\n",
      "Total params: 21,141,841\n",
      "Trainable params: 6,427,153\n",
      "Non-trainable params: 14,714,688\n",
      "_________________________________________________________________\n",
      "None\n"
     ]
    }
   ],
   "source": [
    "from keras.models import Sequential\n",
    "from keras.layers import Dense, Dropout, Activation, Flatten\n",
    "from keras.layers import Conv2D, MaxPooling2D, ZeroPadding2D\n",
    "from keras.layers.normalization import BatchNormalization\n",
    "from keras.models import Model\n",
    "\n",
    "num_classes = 17\n",
    "\n",
    "FC_Head = addTopModel(vgg16, num_classes)\n",
    "\n",
    "model = Model(inputs=vgg16.input, outputs=FC_Head)\n",
    "\n",
    "print(model.summary())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Loading our Flowers Dataset"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Found 1190 images belonging to 17 classes.\n",
      "Found 170 images belonging to 17 classes.\n"
     ]
    }
   ],
   "source": [
    "from keras.preprocessing.image import ImageDataGenerator\n",
    "\n",
    "train_data_dir = './17_flowers/train'\n",
    "validation_data_dir = './17_flowers/validation'\n",
    "\n",
    "train_datagen = ImageDataGenerator(\n",
    "      rescale=1./255,\n",
    "      rotation_range=20,\n",
    "      width_shift_range=0.2,\n",
    "      height_shift_range=0.2,\n",
    "      horizontal_flip=True,\n",
    "      fill_mode='nearest')\n",
    " \n",
    "validation_datagen = ImageDataGenerator(rescale=1./255)\n",
    " \n",
    "# Change the batchsize according to your system RAM\n",
    "train_batchsize = 16\n",
    "val_batchsize = 10\n",
    " \n",
    "train_generator = train_datagen.flow_from_directory(\n",
    "        train_data_dir,\n",
    "        target_size=(img_rows, img_cols),\n",
    "        batch_size=train_batchsize,\n",
    "        class_mode='categorical')\n",
    " \n",
    "validation_generator = validation_datagen.flow_from_directory(\n",
    "        validation_data_dir,\n",
    "        target_size=(img_rows, img_cols),\n",
    "        batch_size=val_batchsize,\n",
    "        class_mode='categorical',\n",
    "        shuffle=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Training our top layers"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/25\n",
      "74/74 [==============================] - 1239s 17s/step - loss: 7.6572 - acc: 0.1689 - val_loss: 2.1551 - val_acc: 0.4800\n",
      "Epoch 2/25\n",
      "74/74 [==============================] - 1070s 14s/step - loss: 2.0316 - acc: 0.3857 - val_loss: 0.7489 - val_acc: 0.7500\n",
      "Epoch 3/25\n",
      "74/74 [==============================] - 1072s 14s/step - loss: 1.5675 - acc: 0.5242 - val_loss: 0.8477 - val_acc: 0.7700\n",
      "Epoch 4/25\n",
      "74/74 [==============================] - 1079s 15s/step - loss: 1.3335 - acc: 0.5786 - val_loss: 1.0377 - val_acc: 0.7300\n",
      "Epoch 5/25\n",
      "74/74 [==============================] - 1079s 15s/step - loss: 1.2145 - acc: 0.6228 - val_loss: 0.4391 - val_acc: 0.8900\n",
      "Epoch 6/25\n",
      "74/74 [==============================] - 1088s 15s/step - loss: 1.0100 - acc: 0.6830 - val_loss: 0.6613 - val_acc: 0.8000\n",
      "Epoch 7/25\n",
      "74/74 [==============================] - 1078s 15s/step - loss: 0.9475 - acc: 0.7112 - val_loss: 0.6876 - val_acc: 0.8200\n",
      "Epoch 8/25\n",
      "74/74 [==============================] - 1100s 15s/step - loss: 0.9103 - acc: 0.7207 - val_loss: 0.6513 - val_acc: 0.8400\n",
      "Epoch 9/25\n",
      "74/74 [==============================] - 1092s 15s/step - loss: 0.8908 - acc: 0.7159 - val_loss: 0.7576 - val_acc: 0.8000\n",
      "Epoch 10/25\n",
      "74/74 [==============================] - 1130s 15s/step - loss: 0.8264 - acc: 0.7424 - val_loss: 0.2326 - val_acc: 0.9500\n",
      "Epoch 11/25\n",
      "74/74 [==============================] - 1112s 15s/step - loss: 0.6928 - acc: 0.7849 - val_loss: 1.0936 - val_acc: 0.7700\n",
      "Epoch 12/25\n",
      "74/74 [==============================] - 1100s 15s/step - loss: 0.8332 - acc: 0.7475 - val_loss: 0.7714 - val_acc: 0.8600\n",
      "Epoch 13/25\n",
      "74/74 [==============================] - 1121s 15s/step - loss: 0.7207 - acc: 0.7756 - val_loss: 0.5933 - val_acc: 0.8600\n",
      "Epoch 14/25\n",
      "74/74 [==============================] - 732s 10s/step - loss: 0.6754 - acc: 0.7956 - val_loss: 0.5916 - val_acc: 0.8500\n",
      "Epoch 15/25\n",
      "74/74 [==============================] - 707s 10s/step - loss: 0.6361 - acc: 0.8092 - val_loss: 0.3061 - val_acc: 0.9000\n",
      "Epoch 16/25\n",
      "74/74 [==============================] - 709s 10s/step - loss: 0.6824 - acc: 0.8125 - val_loss: 0.5642 - val_acc: 0.8200\n",
      "Epoch 17/25\n",
      "74/74 [==============================] - 707s 10s/step - loss: 0.5408 - acc: 0.8198 - val_loss: 0.3972 - val_acc: 0.8700\n",
      "Epoch 18/25\n",
      "74/74 [==============================] - 707s 10s/step - loss: 0.6316 - acc: 0.7990 - val_loss: 0.8962 - val_acc: 0.8000\n",
      "Epoch 19/25\n",
      "74/74 [==============================] - 703s 10s/step - loss: 0.5471 - acc: 0.8395 - val_loss: 1.1561 - val_acc: 0.8500\n",
      "Epoch 20/25\n",
      "74/74 [==============================] - 708s 10s/step - loss: 0.4926 - acc: 0.8449 - val_loss: 0.6417 - val_acc: 0.8700\n",
      "Epoch 21/25\n",
      "74/74 [==============================] - 709s 10s/step - loss: 0.5214 - acc: 0.8288 - val_loss: 0.6306 - val_acc: 0.8400\n",
      "Epoch 22/25\n",
      "74/74 [==============================] - 704s 10s/step - loss: 0.5625 - acc: 0.8331 - val_loss: 0.2320 - val_acc: 0.9300\n",
      "Epoch 23/25\n",
      "74/74 [==============================] - 708s 10s/step - loss: 0.5472 - acc: 0.8325 - val_loss: 0.9466 - val_acc: 0.8200\n",
      "Epoch 24/25\n",
      "74/74 [==============================] - 706s 10s/step - loss: 0.5269 - acc: 0.8457 - val_loss: 0.5448 - val_acc: 0.9100\n",
      "Epoch 25/25\n",
      "74/74 [==============================] - 712s 10s/step - loss: 0.4292 - acc: 0.8702 - val_loss: 0.5852 - val_acc: 0.8300\n"
     ]
    },
    {
     "ename": "NameError",
     "evalue": "name 'x_test' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-6-5284b7a1fcc0>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m     20\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     21\u001b[0m \u001b[0;31m# Evaluate the performance of our trained model\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 22\u001b[0;31m \u001b[0mscores\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmodel\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mevaluate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx_test\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my_test\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mverbose\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     23\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Test loss:'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mscores\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     24\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Test accuracy:'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mscores\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mNameError\u001b[0m: name 'x_test' is not defined"
     ]
    }
   ],
   "source": [
    "from keras.optimizers import RMSprop\n",
    "from keras.callbacks import ModelCheckpoint, EarlyStopping\n",
    "                   \n",
    "checkpoint = ModelCheckpoint(\"/home/deeplearningcv/DeepLearningCV/Trained Models/flowers_vgg.h5\",\n",
    "                             monitor=\"val_loss\",\n",
    "                             mode=\"min\",\n",
    "                             save_best_only = True,\n",
    "                             verbose=1)\n",
    "\n",
    "earlystop = EarlyStopping(monitor = 'val_loss', \n",
    "                          min_delta = 0, \n",
    "                          patience = 3,\n",
    "                          verbose = 1,\n",
    "                          restore_best_weights = True)\n",
    "\n",
    "# we put our call backs into a callback list\n",
    "callbacks = [earlystop, checkpoint]\n",
    "\n",
    "# Note we use a very small learning rate \n",
    "model.compile(loss = 'categorical_crossentropy',\n",
    "              optimizer = RMSprop(lr = 0.001),\n",
    "              metrics = ['accuracy'])\n",
    "\n",
    "nb_train_samples = 1190\n",
    "nb_validation_samples = 170\n",
    "epochs = 3\n",
    "batch_size = 16\n",
    "\n",
    "history = model.fit_generator(\n",
    "    train_generator,\n",
    "    steps_per_epoch = nb_train_samples // batch_size,\n",
    "    epochs = epochs,\n",
    "    callbacks = callbacks,\n",
    "    validation_data = validation_generator,\n",
    "    validation_steps = nb_validation_samples // batch_size)\n",
    "\n",
    "model.save(\"/home/deeplearningcv/DeepLearningCV/Trained Models/flowers_vgg.h5\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Can we speed this up?\n",
    "#### Let's try re-sizing the image to 64 x 64"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0 InputLayer False\n",
      "1 Conv2D False\n",
      "2 Conv2D False\n",
      "3 MaxPooling2D False\n",
      "4 Conv2D False\n",
      "5 Conv2D False\n",
      "6 MaxPooling2D False\n",
      "7 Conv2D False\n",
      "8 Conv2D False\n",
      "9 Conv2D False\n",
      "10 MaxPooling2D False\n",
      "11 Conv2D False\n",
      "12 Conv2D False\n",
      "13 Conv2D False\n",
      "14 MaxPooling2D False\n",
      "15 Conv2D False\n",
      "16 Conv2D False\n",
      "17 Conv2D False\n",
      "18 MaxPooling2D False\n"
     ]
    }
   ],
   "source": [
    "from keras.applications import VGG16\n",
    "\n",
    "# Setting the input size now to 64 x 64 pixel \n",
    "img_rows = 64\n",
    "img_cols = 64 \n",
    "\n",
    "# Re-loads the VGG16 model without the top or FC layers\n",
    "vgg16 = VGG16(weights = 'imagenet', \n",
    "                 include_top = False, \n",
    "                 input_shape = (img_rows, img_cols, 3))\n",
    "\n",
    "# Here we freeze the last 4 layers \n",
    "# Layers are set to trainable as True by default\n",
    "for layer in vgg16.layers:\n",
    "    layer.trainable = False\n",
    "    \n",
    "# Let's print our layers \n",
    "for (i,layer) in enumerate(vgg16.layers):\n",
    "    print(str(i) + \" \"+ layer.__class__.__name__, layer.trainable)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Let's create our new model using an image size of 64 x 64"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Found 1190 images belonging to 17 classes.\n",
      "Found 170 images belonging to 17 classes.\n",
      "_________________________________________________________________\n",
      "Layer (type)                 Output Shape              Param #   \n",
      "=================================================================\n",
      "input_7 (InputLayer)         (None, 64, 64, 3)         0         \n",
      "_________________________________________________________________\n",
      "block1_conv1 (Conv2D)        (None, 64, 64, 64)        1792      \n",
      "_________________________________________________________________\n",
      "block1_conv2 (Conv2D)        (None, 64, 64, 64)        36928     \n",
      "_________________________________________________________________\n",
      "block1_pool (MaxPooling2D)   (None, 32, 32, 64)        0         \n",
      "_________________________________________________________________\n",
      "block2_conv1 (Conv2D)        (None, 32, 32, 128)       73856     \n",
      "_________________________________________________________________\n",
      "block2_conv2 (Conv2D)        (None, 32, 32, 128)       147584    \n",
      "_________________________________________________________________\n",
      "block2_pool (MaxPooling2D)   (None, 16, 16, 128)       0         \n",
      "_________________________________________________________________\n",
      "block3_conv1 (Conv2D)        (None, 16, 16, 256)       295168    \n",
      "_________________________________________________________________\n",
      "block3_conv2 (Conv2D)        (None, 16, 16, 256)       590080    \n",
      "_________________________________________________________________\n",
      "block3_conv3 (Conv2D)        (None, 16, 16, 256)       590080    \n",
      "_________________________________________________________________\n",
      "block3_pool (MaxPooling2D)   (None, 8, 8, 256)         0         \n",
      "_________________________________________________________________\n",
      "block4_conv1 (Conv2D)        (None, 8, 8, 512)         1180160   \n",
      "_________________________________________________________________\n",
      "block4_conv2 (Conv2D)        (None, 8, 8, 512)         2359808   \n",
      "_________________________________________________________________\n",
      "block4_conv3 (Conv2D)        (None, 8, 8, 512)         2359808   \n",
      "_________________________________________________________________\n",
      "block4_pool (MaxPooling2D)   (None, 4, 4, 512)         0         \n",
      "_________________________________________________________________\n",
      "block5_conv1 (Conv2D)        (None, 4, 4, 512)         2359808   \n",
      "_________________________________________________________________\n",
      "block5_conv2 (Conv2D)        (None, 4, 4, 512)         2359808   \n",
      "_________________________________________________________________\n",
      "block5_conv3 (Conv2D)        (None, 4, 4, 512)         2359808   \n",
      "_________________________________________________________________\n",
      "block5_pool (MaxPooling2D)   (None, 2, 2, 512)         0         \n",
      "_________________________________________________________________\n",
      "flatten (Flatten)            (None, 2048)              0         \n",
      "_________________________________________________________________\n",
      "dense_5 (Dense)              (None, 256)               524544    \n",
      "_________________________________________________________________\n",
      "dropout_3 (Dropout)          (None, 256)               0         \n",
      "_________________________________________________________________\n",
      "dense_6 (Dense)              (None, 17)                4369      \n",
      "=================================================================\n",
      "Total params: 15,243,601\n",
      "Trainable params: 528,913\n",
      "Non-trainable params: 14,714,688\n",
      "_________________________________________________________________\n",
      "None\n"
     ]
    }
   ],
   "source": [
    "from keras.applications import VGG16\n",
    "from keras.models import Sequential\n",
    "from keras.layers import Dense, Dropout, Activation, Flatten\n",
    "from keras.layers import Conv2D, MaxPooling2D, ZeroPadding2D\n",
    "from keras.layers.normalization import BatchNormalization\n",
    "from keras.models import Model\n",
    "from keras.optimizers import RMSprop\n",
    "from keras.preprocessing.image import ImageDataGenerator\n",
    "\n",
    "train_data_dir = './17_flowers/train'\n",
    "validation_data_dir = './17_flowers/validation'\n",
    "\n",
    "train_datagen = ImageDataGenerator(\n",
    "      rescale=1./255,\n",
    "      rotation_range=20,\n",
    "      width_shift_range=0.2,\n",
    "      height_shift_range=0.2,\n",
    "      horizontal_flip=True,\n",
    "      fill_mode='nearest')\n",
    " \n",
    "validation_datagen = ImageDataGenerator(rescale=1./255)\n",
    " \n",
    "# Change the batchsize according to your system RAM\n",
    "train_batchsize = 16\n",
    "val_batchsize = 10\n",
    " \n",
    "train_generator = train_datagen.flow_from_directory(\n",
    "        train_data_dir,\n",
    "        target_size=(img_rows, img_cols),\n",
    "        batch_size=train_batchsize,\n",
    "        class_mode='categorical')\n",
    " \n",
    "validation_generator = validation_datagen.flow_from_directory(\n",
    "        validation_data_dir,\n",
    "        target_size=(img_rows, img_cols),\n",
    "        batch_size=val_batchsize,\n",
    "        class_mode='categorical',\n",
    "        shuffle=False)\n",
    "\n",
    "# Re-loads the VGG16 model without the top or FC layers\n",
    "vgg16 = VGG16(weights = 'imagenet', \n",
    "                 include_top = False, \n",
    "                 input_shape = (img_rows, img_cols, 3))\n",
    "\n",
    "# Freeze layers\n",
    "for layer in vgg16.layers:\n",
    "    layer.trainable = False\n",
    "    \n",
    "# Number of classes in the Flowers-17 dataset\n",
    "num_classes = 17\n",
    "\n",
    "FC_Head = addTopModel(vgg16, num_classes)\n",
    "\n",
    "model = Model(inputs=vgg16.input, outputs=FC_Head)\n",
    "\n",
    "print(model.summary())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Training using 64 x 64 image size is MUCH faster!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/25\n",
      "37/37 [==============================] - 19s 510ms/step - loss: 0.5119 - acc: 0.8328 - val_loss: 0.7950 - val_acc: 0.7800\n",
      "\n",
      "Epoch 00001: val_loss improved from inf to 0.79499, saving model to /home/deeplearningcv/DeepLearningCV/Trained Models/flowers_vgg_64.h5\n",
      "Epoch 2/25\n",
      "37/37 [==============================] - 18s 482ms/step - loss: 0.4786 - acc: 0.8306 - val_loss: 1.0675 - val_acc: 0.6600\n",
      "\n",
      "Epoch 00002: val_loss did not improve from 0.79499\n",
      "Epoch 3/25\n",
      "37/37 [==============================] - 18s 496ms/step - loss: 0.4051 - acc: 0.8615 - val_loss: 0.9847 - val_acc: 0.7800\n",
      "\n",
      "Epoch 00003: val_loss did not improve from 0.79499\n",
      "Epoch 4/25\n",
      "37/37 [==============================] - 18s 497ms/step - loss: 0.4178 - acc: 0.8733 - val_loss: 0.7770 - val_acc: 0.8000\n",
      "\n",
      "Epoch 00004: val_loss improved from 0.79499 to 0.77698, saving model to /home/deeplearningcv/DeepLearningCV/Trained Models/flowers_vgg_64.h5\n",
      "Epoch 5/25\n",
      "37/37 [==============================] - 19s 505ms/step - loss: 0.5334 - acc: 0.8277 - val_loss: 0.6631 - val_acc: 0.7600\n",
      "\n",
      "Epoch 00005: val_loss improved from 0.77698 to 0.66309, saving model to /home/deeplearningcv/DeepLearningCV/Trained Models/flowers_vgg_64.h5\n",
      "Epoch 6/25\n",
      "37/37 [==============================] - 19s 516ms/step - loss: 0.3980 - acc: 0.8553 - val_loss: 1.2081 - val_acc: 0.7200\n",
      "\n",
      "Epoch 00006: val_loss did not improve from 0.66309\n",
      "Epoch 7/25\n",
      "37/37 [==============================] - 20s 542ms/step - loss: 0.3961 - acc: 0.8699 - val_loss: 0.5652 - val_acc: 0.8600\n",
      "\n",
      "Epoch 00007: val_loss improved from 0.66309 to 0.56520, saving model to /home/deeplearningcv/DeepLearningCV/Trained Models/flowers_vgg_64.h5\n",
      "Epoch 8/25\n",
      "37/37 [==============================] - 20s 535ms/step - loss: 0.4706 - acc: 0.8374 - val_loss: 0.6537 - val_acc: 0.8000\n",
      "\n",
      "Epoch 00008: val_loss did not improve from 0.56520\n",
      "Epoch 9/25\n",
      "37/37 [==============================] - 21s 563ms/step - loss: 0.3928 - acc: 0.8682 - val_loss: 1.1079 - val_acc: 0.7000\n",
      "\n",
      "Epoch 00009: val_loss did not improve from 0.56520\n",
      "Epoch 10/25\n",
      "37/37 [==============================] - 20s 539ms/step - loss: 0.4136 - acc: 0.8564 - val_loss: 0.9631 - val_acc: 0.8000\n",
      "\n",
      "Epoch 00010: val_loss did not improve from 0.56520\n",
      "\n",
      "Epoch 00010: ReduceLROnPlateau reducing learning rate to 1.9999999494757503e-05.\n",
      "Epoch 11/25\n",
      "37/37 [==============================] - 20s 543ms/step - loss: 0.3742 - acc: 0.8885 - val_loss: 0.8073 - val_acc: 0.7800\n",
      "\n",
      "Epoch 00011: val_loss did not improve from 0.56520\n",
      "Epoch 12/25\n",
      "37/37 [==============================] - 20s 541ms/step - loss: 0.4287 - acc: 0.8564 - val_loss: 0.5695 - val_acc: 0.8200\n",
      "Restoring model weights from the end of the best epoch\n",
      "\n",
      "Epoch 00012: val_loss did not improve from 0.56520\n",
      "Epoch 00012: early stopping\n"
     ]
    }
   ],
   "source": [
    "from keras.optimizers import RMSprop\n",
    "from keras.callbacks import ModelCheckpoint, EarlyStopping, ReduceLROnPlateau\n",
    "                   \n",
    "checkpoint = ModelCheckpoint(\"/home/deeplearningcv/DeepLearningCV/Trained Models/flowers_vgg_64.h5\",\n",
    "                             monitor=\"val_loss\",\n",
    "                             mode=\"min\",\n",
    "                             save_best_only = True,\n",
    "                             verbose=1)\n",
    "\n",
    "earlystop = EarlyStopping(monitor = 'val_loss', \n",
    "                          min_delta = 0, \n",
    "                          patience = 5,\n",
    "                          verbose = 1,\n",
    "                          restore_best_weights = True)\n",
    "\n",
    "reduce_lr = ReduceLROnPlateau(monitor = 'val_loss',\n",
    "                              factor = 0.2,\n",
    "                              patience = 3,\n",
    "                              verbose = 1,\n",
    "                              min_delta = 0.00001)\n",
    "\n",
    "# we put our call backs into a callback list\n",
    "callbacks = [earlystop, checkpoint, reduce_lr]\n",
    "\n",
    "# Note we use a very small learning rate \n",
    "model.compile(loss = 'categorical_crossentropy',\n",
    "              optimizer = RMSprop(lr = 0.0001),\n",
    "              metrics = ['accuracy'])\n",
    "\n",
    "nb_train_samples = 1190\n",
    "nb_validation_samples = 170\n",
    "epochs = 25\n",
    "batch_size = 32\n",
    "\n",
    "history = model.fit_generator(\n",
    "    train_generator,\n",
    "    steps_per_epoch = nb_train_samples // batch_size,\n",
    "    epochs = epochs,\n",
    "    callbacks = callbacks,\n",
    "    validation_data = validation_generator,\n",
    "    validation_steps = nb_validation_samples // batch_size)\n",
    "\n",
    "model.save(\"/home/deeplearningcv/DeepLearningCV/Trained Models/flowers_vgg_64.h5\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
